#include "yk_YKComics_Test_Plugin_PDFHandler.h"

#include <android/bitmap.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "fitz.h"
#include "mupdf.h"

#define LOG_TAG "libmupdf"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

/* Set to 1 to enable debug log traces. */
#define DEBUG 0

/* Enable to log rendering times (render each frame 100 times and time) */
#undef TIME_DISPLAY_LIST

#define MAX_SEARCH_HITS (500)

/* Globals */
fz_colorspace *colorspace;
fz_document *doc;
int pagenum = 1;
int resolution = 160;
float pageWidth = 100;
float pageHeight = 100;
fz_display_list *currentPageList;
fz_rect currentMediabox;
fz_context *ctx;
int currentPageNumber = -1;
fz_page *currentPage = NULL;
fz_bbox *hit_bbox = NULL;


jint JNICALL Java_yk_YKComics_Test_Plugin_PDFHandler_OpenFile(JNIEnv* _env, jclass _class, jstring jfilename )
{
	const char *filename;
	int pages = 0;
	int result = 0;

	filename = (*_env)->GetStringUTFChars(_env, jfilename, NULL);
	if (filename == NULL) {
		LOGE("Failed to get filename");
		return 0;
	}

	/* 128 MB store for low memory devices. Tweak as necessary. */
	ctx = fz_new_context(NULL, NULL, 128 << 20);
	if (!ctx) {
		LOGE("Failed to initialise context");
		return 0;
	}

	doc = NULL;
	fz_try(ctx)
	{
		colorspace = fz_device_rgb;

		LOGE("Opening document...");
		fz_try(ctx)
		{
			doc = fz_open_document(ctx, (char *) filename);
		}
		fz_catch(ctx)
		{
			fz_throw(ctx, "Cannot open document: '%s'\n", filename);
		}
		LOGE("Done!");
		result = 1;
	}
	fz_catch(ctx)
	{
		LOGE("Failed: %s", ctx->error->message);
		fz_close_document(doc);
		doc = NULL;
		fz_free_context(ctx);
		ctx = NULL;
	}

	(*_env)->ReleaseStringUTFChars(_env, jfilename, filename);

	return result;
}

void JNICALL Java_yk_YKComics_Test_Plugin_PDFHandler_CloseFile(JNIEnv* _env, jclass _class)
{
	fz_free(ctx, hit_bbox);
	hit_bbox = NULL;
	fz_free_display_list(ctx, currentPageList);
	currentPageList = NULL;
	if (currentPage != NULL)
	{
		fz_free_page(doc, currentPage);
		currentPage = NULL;
	}
	fz_close_document(doc);
	doc = NULL;
}

void JNICALL Java_yk_YKComics_Test_Plugin_PDFHandler_MovePage(JNIEnv* _env, jclass _class, jint page )
{
	float zoom;
	fz_matrix ctm;
	fz_bbox bbox;
	fz_device *dev = NULL;

	fz_var(dev);

	if (currentPage != NULL && page != currentPageNumber)
	{
		fz_free_page(doc, currentPage);
		currentPage = NULL;
	}

	/* In the event of an error, ensure we give a non-empty page */
	pageWidth = 100;
	pageHeight = 100;

	currentPageNumber = page;
	LOGE("Goto page %d...", page);
	fz_try(ctx)
	{
		if (currentPageList != NULL)
		{
			fz_free_display_list(ctx, currentPageList);
			currentPageList = NULL;
		}
		pagenum = page;
		currentPage = fz_load_page(doc, pagenum);
		zoom = resolution / 72;
		currentMediabox = fz_bound_page(doc, currentPage);
		ctm = fz_scale(zoom, zoom);
		bbox = fz_round_rect(fz_transform_rect(ctm, currentMediabox));
		pageWidth = bbox.x1-bbox.x0;
		pageHeight = bbox.y1-bbox.y0;
	}
	fz_catch(ctx)
	{
		currentPageNumber = page;
		LOGE("cannot make displaylist from page %d", pagenum);
	}

	fz_free_device(dev);
	dev = NULL;
}

jint JNICALL Java_yk_YKComics_Test_Plugin_PDFHandler_GetTotalPage(JNIEnv* _env, jclass _class)
{
	return fz_count_pages(doc);
}

jfloat JNICALL Java_yk_YKComics_Test_Plugin_PDFHandler_GetPageWidth(JNIEnv* _env, jclass _class)
{
	return pageWidth;
}

jfloat JNICALL Java_yk_YKComics_Test_Plugin_PDFHandler_GetPageHeight(JNIEnv* _env, jclass _class)
{
	return pageHeight;
}

jint JNICALL Java_yk_YKComics_Test_Plugin_PDFHandler_DrawPage(JNIEnv* _env, jclass _class, jobject bitmap, int pageW, int pageH, int patchX, int patchY, int patchW, int patchH)
{
	AndroidBitmapInfo info;
		void *pixels;
		int ret;
		fz_device *dev = NULL;
		float zoom;
		fz_matrix ctm;
		fz_bbox bbox;
		fz_pixmap *pix = NULL;
		float xscale, yscale;
		fz_bbox rect;

		fz_var(pix);
		fz_var(dev);

		LOGI("In native method\n");
		if ((ret = AndroidBitmap_getInfo(_env, bitmap, &info)) < 0) {
			LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
			return 0;
		}

		LOGI("Checking format\n");
		if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
			LOGE("Bitmap format is not RGBA_8888 !");
			return 0;
		}

		LOGI("locking pixels\n");
		if ((ret = AndroidBitmap_lockPixels(_env, bitmap, &pixels)) < 0) {
			LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
			return 0;
		}

		/* Call mupdf to render display list to screen */
		LOGE("Rendering page=%dx%d patch=[%d,%d,%d,%d]",
				pageW, pageH, patchX, patchY, patchW, patchH);

		fz_try(ctx)
		{
			if (currentPageList == NULL)
			{
				/* Render to list */
				currentPageList = fz_new_display_list(ctx);
				dev = fz_new_list_device(ctx, currentPageList);
				fz_run_page(doc, currentPage, dev, fz_identity, NULL);
			}
			rect.x0 = patchX;
			rect.y0 = patchY;
			rect.x1 = patchX + patchW;
			rect.y1 = patchY + patchH;
			pix = fz_new_pixmap_with_bbox_and_data(ctx, colorspace, rect, pixels);
			if (currentPageList == NULL)
			{
				fz_clear_pixmap_with_value(ctx, pix, 0xd0);
				break;
			}
			fz_clear_pixmap_with_value(ctx, pix, 0xff);

			zoom = resolution / 72;
			ctm = fz_scale(zoom, zoom);
			bbox = fz_round_rect(fz_transform_rect(ctm,currentMediabox));
			/* Now, adjust ctm so that it would give the correct page width
			 * heights. */
			xscale = (float)pageW/(float)(bbox.x1-bbox.x0);
			yscale = (float)pageH/(float)(bbox.y1-bbox.y0);
			ctm = fz_concat(ctm, fz_scale(xscale, yscale));
			bbox = fz_round_rect(fz_transform_rect(ctm,currentMediabox));
			dev = fz_new_draw_device(ctx, pix);
	#ifdef TIME_DISPLAY_LIST
			{
				clock_t time;
				int i;

				LOGE("Executing display list");
				time = clock();
				for (i=0; i<100;i++) {
	#endif
					fz_run_display_list(currentPageList, dev, ctm, bbox, NULL);
	#ifdef TIME_DISPLAY_LIST
				}
				time = clock() - time;
				LOGE("100 renders in %d (%d per sec)", time, CLOCKS_PER_SEC);
			}
	#endif
			fz_free_device(dev);
			dev = NULL;
			fz_drop_pixmap(ctx, pix);
			LOGE("Rendered");
		}
		fz_catch(ctx)
		{
			fz_free_device(dev);
			LOGE("Render failed");
		}

		AndroidBitmap_unlockPixels(_env, bitmap);

		return 1;
}
